package model.oficinaDeAlumnos;

import model.alumnos.Alumno;
import model.alumnos.AlumnoEnCarrera;
import model.carrerasYPlanes.Carrera;
import model.carrerasYPlanes.PlanDeEstudio;
import model.interfaces.AlumnoIMPL;
import model.interfaces.MateriaIMPL;
import model.interfaces.OficinaAlumnosIMPL;
import model.materiasYCatedras.AlumnoEnCatedra;
import model.materiasYCatedras.Catedra;
import model.materiasYCatedras.MateriaPorPlanDeEstudio;

import java.util.ArrayList;
import java.util.List;

public class OficinaDeAlumnos implements OficinaAlumnosIMPL{

	private static OficinaDeAlumnos instancia;
	
	private List<TramiteEquivalencia> tramites;
	private int legajo = 0;

	private OficinaDeAlumnos(){
		super();
		this.tramites = new ArrayList<TramiteEquivalencia>();
	}
	
	public static OficinaDeAlumnos getInstance(){
		if (instancia == null){
			instancia = new OficinaDeAlumnos();
		}
		return instancia;
	}
	
	/**
	 * Agrega un tramite de equivalencia a la lista "tramites" de la oficina de alumno
	 *
	 */
	public void agregarTramite(TramiteEquivalencia tramite){
		this.tramites.add(tramite);
	}


	/**
	 * Devuelve el proximo legajo a asignar, incrementando en 1 unidad para que
	 * no se repita ningun legajo en toda la universidad
	 */
	public int getProximoLegajo(){
		this.setLegajo(this.getLegajo() + 1);
		return this.getLegajo();
	}
	
	/**
	 * Crea una instancia de alumnoEnCarrera y se la asigna a alumno, luego agrega
	 * ese alumno a la carrera pasada por parametro
	 * @param unAlumno
	 * @param unaCarrera
	 */
	public void inscribirAlumnoEnCarrera(Alumno unAlumno, Carrera unaCarrera){
		PlanDeEstudio plan = unaCarrera.getPlanActual();
		AlumnoEnCarrera alumnoAInscribir = new AlumnoEnCarrera(unAlumno, plan, this.getProximoLegajo());
		unAlumno.agregarEnCarrera(alumnoAInscribir);
	}
	
	/**
	 * Comprueba que haya algun cupo en todas las catedras de la materia
	 * por plan de estudio
	 * @param unaMateria
	 * @return
	 */
	public boolean hayLugarEnLaMateria(MateriaPorPlanDeEstudio unaMateria){
		for(Catedra unaCatedra : unaMateria.getCatedras()){
			if(unaCatedra.tieneCupo()){
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Comprueba si al alumno pasado por parametro se le superponen los horarios 
	 * de las catedras en las que esta acutualmente inscripto con la catedra
	 * pasada por parametro 
	 *
	 */
	public boolean alAlumnoSeLeSuperponeLaCatedra(Catedra catedra, AlumnoEnCarrera unAlumno){
		return catedra.seSuperponenHorariosCatedras(unAlumno.getCatedrasInscripto());
	}
	
	
	/**
	 * Comprueba que se den todas las condiciones para las cuales un alumno
	 * se pueda inscribir en una materia por plan de estudio, estas son que haya lugar en la materia,
	 * que el alumno tenga aprobadas todas las correlativas, que el alumno sea regular,
	 * que el alumno no este ya inscripto en otra catedra de la misma materia y que sea una
	 * materia del plan de estudio el cual esta anotado
	 * @param unaMateria
	 * @param unAlumno
	 * @return
	 */
	public boolean puedeInscribirseAlumnoEnMateria(MateriaPorPlanDeEstudio unaMateria, AlumnoEnCarrera unAlumno){
		return (this.hayLugarEnLaMateria(unaMateria) 
				&& unAlumno.tengoAprobadasTodasLasCorrelativas(unaMateria) 
				&& unAlumno.soyRegular() 
				&& !unAlumno.estaInscriptoEnMateria(unaMateria)
				&& unAlumno.esUnaMateriaDeMiPlanDeEstudio(unaMateria));
	}
	
	/**
	 * Comprueba si el alumno pasado por parametro se puede inscribir en una catedra comprobando
	 * que al alumno no se le superpongan los horarios y que la catedra tenga cupo
	 *
	 */
	public boolean puedeInscribirseAlumnoEnCatedra(Catedra catedra, AlumnoEnCarrera unAlumno){
		return !this.alAlumnoSeLeSuperponeLaCatedra(catedra, unAlumno)
				&& catedra.tieneCupo();
	}
	
	/**
	 * Devuelve la primer catedra que tenga cupo de la materia pasada por parametro
	 * @param unaMateria
	 * @return
	 */
	public Catedra devolverCatedraAInscribir(MateriaPorPlanDeEstudio unaMateria){
		for(Catedra unaCatedra : unaMateria.getCatedras()){
			if(unaCatedra.tieneCupo()){
				return unaCatedra;
			}
		}
		return null;
	}
	
	/**
	 * Anota al alumno en carrera en una materia por plan de estudio, comprobando que cumpla
	 * los requisitos, en caso contrario salta una exception
	 *
	 */
	public void anotarAlumnoEnMateria(MateriaPorPlanDeEstudio unaMateria, AlumnoEnCarrera unAlumno){
		if(this.puedeInscribirseAlumnoEnMateria(unaMateria, unAlumno)){
			Catedra catedraAInscribir = this.devolverCatedraAInscribir(unaMateria);
			catedraAInscribir.inscribirAlumno(unAlumno);
			unAlumno.agregarCatedrasInscripto(catedraAInscribir);
		}
		else{
			throw new RuntimeException("El alumno no puede anotarse en la materia");
		}
	}
	
	/**
	 * Anota al alumno en carrera en una catedra, comprobando que cumpla
	 * los requisitos, en caso contrario salta una exception
	 *
	 */	
	public void inscribirAlumnoEnCatedra(Catedra unaCatedra, AlumnoEnCarrera unAlumno){
		if(this.puedeInscribirseAlumnoEnCatedra(unaCatedra, unAlumno)){
			unaCatedra.inscribirAlumno(unAlumno);
			unAlumno.agregarCatedrasInscripto(unaCatedra);
		}else{
			throw new RuntimeException("No se puede inscribir al alumno en la catedra");
		}
	}
	
	
	/**
	 *Borra al alumno en catedra de la catedra pasada por parametro
	 *
	 */
	public void borrarAlumnoDeCatedra(Catedra unaCatedra, AlumnoEnCatedra unAlumno){
		if(unaCatedra.getMateria().getCatedras().contains(unAlumno)){
			unaCatedra.getMateria().getCatedras().remove(unAlumno);
		}
	}
	
	/**
	 *En caso de ser posible cambia al alumno de catedra, en caso contrario
	 *salta una exception
	 *
	 */
	public void cambiarAlumnoDeCatedra(Catedra unaCatedra, AlumnoEnCatedra unAlumno){
		if(unaCatedra.tieneCupo()){
			this.borrarAlumnoDeCatedra(unAlumno.getCatedra(), unAlumno);
			unaCatedra.getAlumnos().add(unAlumno);
			unAlumno.getAlumno().getCatedrasInscripto().add(unaCatedra);
		}
		else{
			throw new RuntimeException("No se puede cambiar al alumo de catedra porque no hay mas cupo");
		}
	}

	@Override
	public String analiticoDe(AlumnoIMPL alumno) {
//		Alumno alu = (Alumno) alumno;
//		return (alu.getAlumnoEnCarrera().get(0)).analitico();
		return this.analitico((Alumno)alumno);
	}

	@Override
	public float coeficienteDe(AlumnoIMPL alumno) {
		Alumno alu = (Alumno) alumno;
		return this.coeficiente(alu.getAlumnoEnCarrera().get(0)).floatValue();
	}
	
	/**
	 *Calcula el coeficiente del alumno en carrera pasado por parametro
	 *
	 */
	public Double coeficiente(AlumnoEnCarrera unAlumno){
		return ((unAlumno.promedio()/2) +
				(( unAlumno.getCreditosPlanDeEstudio() - unAlumno.creditosFaltantes())
				  / (double) unAlumno.getCreditosPlanDeEstudio()) * 5);		
	}
	
	public String analitico(Alumno alumno){
		StringBuffer str = new StringBuffer();
		for (AlumnoEnCarrera aluCarr : alumno.getAlumnoEnCarrera()) {
			str.append("* " + aluCarr.getCarrera().getNombre());
			str.append("\n");
			str.append(aluCarr.analitico());
		}
		return str.toString();
	}
	
	//GETTERS Y SETTERS:
	public List<TramiteEquivalencia> getTramites() {
		return tramites;
	}

	public void setTramites(List<TramiteEquivalencia> tramites) {
		this.tramites = tramites;
	}

	public int getLegajo() {
		return legajo;
	}

	public void setLegajo(int legajo) {
		this.legajo = legajo;
	}	
	
	
}
